#ifndef _FORK_BOX_H_JM_20110830_
#define _FORK_BOX_H_JM_20110830_
/**@file ForkBox.h
 * Rozdelenie vypoctu do viacerych vetiev.
 **/


// ##
#include "RootBox.h"
#include "input/HardCodedDataInputBox.h"

// ## C++
#include <vector>
// ## bobox
#include <bobox_box.hpp>
#include <bobox_types.hpp>

namespace jm4bbx {
	/**
	* Popis IO pre ForkBox.
	**/
	struct ForkBoxDescriptor {
		static const char* BOX_ID;
		static const groupType INPUT_GROUP;
		//static const groupType INPUT_DATA_GROUP;
		static const groupType OUTPUT_GROUP_BEGIN;	
		static const columnType INPUT_COLUMN;
		static const columnType OUTPUT_COLUMN;
		static const inType IN_ARC;
		//static const inType IN_DATA_ARC;
		static const outType OUT_ARC_BEGIN;
		static const char* FORK_FACTOR_PARAM_NAME;
		static const char* HAS_DATA_PARAM_NAME;
		//static const char* HAS_START_SIGNAL_PARAM_NAME;

		static outType getOutArc(int index);
		static groupType getOutGroup(int index);
	};

	/**
	 * ForkBox zabezpecuje rovetvenie vypoctu.
	 * Rozvetvenie moze byt fyzicke alebo logicke.
	 * V pripade fyzickeho rozvetvenia ma box na vstupe data, ktore rovnomerne
	 * rozdeli do novovzniknutych vetiev vypoctu.
	 * Pri logickom vetveni sa vetvi len vypocet bez potreby rozdelovat data.
	 * Toto chovanie je urcene parametrom boxu v definicii modelu vypoctu.
	 * Tento parameter ma nazov <i>hasData</i>.
	 * 
	 * Parameter <i>hasData</i> urcuje vetvenie dat. Hodnota 0 nie su data pre
	 * vetvenie v pripade indej hodnoty su data pre vetvenie. Defaultne pri 
	 * nezadani parametru do modelu sa ocakavaju data na vetvenie. Vetvenie dat 
	 * prebieha tak aby kazda vetva mala co najpodobnejsi pocet dat. 	 
	 *  
	 * Pocet vetiev do ktorych box rozdeli vypocet je urceny pomocou 
	 * parameteru <i>forkFactor</i>. Jedna sa o defakto povinny parameter.
	 * Pri vynechani parametru <i>forkFactor</i> z definicie 
	 * modelu sa pouzije <i>forkFactor</i>=1, co znamena ze neprebehne ziadne 
	 * vetvenie.
	 * 
	 * Box naviac umoznuje vyuzit vstupne signaly ako rozsirenie moznosti cakania 
	 * na vypocet. Vstupne signaly su realizovane v sulade so vstupnymi signalmi 
	 * z {@link Box}. Vystupne signaly ForkBox nepodporuje.
	 * 
	 * Vypocet sa rozvetvi do N vetiev == vystupnych skupin, pricom N je urcene
	 * parametrom boxu <i>forkFactor</i>.
	 **/
	class ForkBox : public Box/*<ForkBoxDescriptor>*/ {
	public:
		typedef ForkBoxDescriptor BoxIO;

		ForkBox(bobox::box_id_type id, bobox::box_tid_type tid, 
			bobox::request *req, const bobox::parameters &params) 
			: Box(id, tid, req, params) 
			, forkFactor_(1)
			, hasData_(true)
		{

				if (params.has_parameter(BoxIO::FORK_FACTOR_PARAM_NAME)) {
					params.get_parameter(BoxIO::FORK_FACTOR_PARAM_NAME, forkFactor_);
				}

				hasData_ = 
					loadBoxBoolParameter(params, BoxIO::HAS_DATA_PARAM_NAME, true);
		}


		virtual void initImpl() override {

			if (hasData_) {
				add_group(BoxIO::INPUT_GROUP, 
					BoxIO::INPUT_COLUMN, 
					get_input_descriptors(BoxIO::IN_ARC));
			}

			for (int i = 0; i < forkFactor_; ++i) {

				add_group(BoxIO::getOutGroup(i), 
					BoxIO::OUTPUT_COLUMN, 
					get_output_descriptors(BoxIO::getOutArc(i)));
			}

			wait4Receive();
		}


		virtual void sync_mach_etwas() override {

			/* input_box rozosiela prvu spravu do modelu vypoctu a ta je 
			 * poisoned. Takze fork na zaciatku vypoctu by hned ukoncil 
			 * bypocet modelu. */
			
			//if (get_group(BoxIO::INPUT_GROUP).poisoned_) {
			//	for (int i = 0; i <  + forkFactor_; ++i) {
			//		send_poisoned(BoxIO::getOutArc(i));
			//	}
			//	return;
			//}

		
			int dataSize = 0;
			if (hasData_) {
				dataSize = get_group(BoxIO::INPUT_GROUP).getReadEnd();//rdend_;
			}
			const int chunkSize = dataSize / forkFactor_; 
			int residue = dataSize - chunkSize*forkFactor_;

			int from = 0;
			int to = chunkSize + getResidue(residue);

			for (int i = 0; i < forkFactor_ - 1; ++i) {
				Group og = get_group(BoxIO::getOutGroup(i));


				og.setWriteBegin(from);//wrbegin_ = from;
				og.setWritePosition(to);//wrpos_ = to;
				og.setWriteEnd(to);//wrend_ = to;


				send(BoxIO::getOutArc(i), BoxIO::getOutGroup(i));

				from = to;
				to += chunkSize +  getResidue(residue);;
			}

			Group og = get_group(groupType(forkFactor_-1));

			to = dataSize;


			og.setWriteBegin(from);//wrbegin_ = from;
			og.setWritePosition(to);//wrpos_ = to;
			og.setWriteEnd(to);//wrend_ = to;

			send(outType(forkFactor_-1), groupType(forkFactor_-1));

			sendSignalsOut();

			wait4Receive();
		}

		void wait4Receive() {
			wait4SignalsIn();
			if (hasData_) {
				receive(BoxIO::IN_ARC, BoxIO::INPUT_GROUP);
			}
		}
	private:
		int forkFactor_;
		bool hasData_;
		//bool hasStartSignal_;

		int getResidue(int& residue) {
			if (residue > 0) {
				--residue;
				return 1;
			} 
			else {
				return 0;
			}
		}

	};
} //    namespace jm4bbx
#endif
//
